从逆向接口到 Web 界面:我的明月浩空音乐批量下载工具
前言
最近我把明月浩空(myhkw.cn)上零散听歌、手动下载的流程,整理成了一个可以批量下载的工具:myhkw-downloader。它同时提供网页界面和命令行两种用法,代码已经开源在 GitHub。
这篇文章记录我为什么会做它、它大概是怎么工作的,以及几个我自己觉得还算有意思的设计。
为什么会做这个工具
明月浩空本身是一个聚合了网易云、QQ 音乐、酷狗、酷我多个来源的在线播放器,单首歌试听、下载都很方便。问题出在”批量”上:当我想把某个歌手的全部作品、某一张专辑,或者一个几百首歌的歌单一次性拉下来时,一首一首点下载就非常折磨人了。
而且手工下载还有几个绕不开的麻烦:同一首歌在好几个平台都能搜到,到底下哪个?有些来源返回的其实是几十秒的试听片段或者静音占位,下回来根本不能用;下一半断了还得从头再来。
于是我想到,干脆把”搜索 → 挑来源 → 下载 → 去重 → 整理”这一整套流程自动化,让我只需要说清楚”要谁的、要多少”,剩下的交给脚本。
它能做什么
简单列一下现在支持的能力:
- 四种下载模式:按歌手、按专辑、按关键词、按歌单 ID。
- 多平台来源聚合:网易 / QQ / 酷狗 / 酷我,某个来源失败会自动换下一个。
- 自动跳过短音频:默认跳过时长不足 1 分钟的片段(阈值可调),并且优先换别的平台找完整版。
- 跨来源去重:同一首歌在多个平台重复出现时只保留一个版本;再叠加持久化索引和文件内容 MD5 双重去重,重复运行不会重新下载。
- 断点续传、请求节流、可选 LRC 歌词。
- Web 界面:任务实时日志与进度条、下载前预览清单、音乐库管理与内置播放器试听。
两种用法
网页界面
对不想记命令的人来说,网页是最省事的入口:
1 | # 1. 安装依赖(requests + flask) |
浏览器访问 http://127.0.0.1:8765 就能用。整个界面分成四块:
| 区域 | 功能 |
|---|---|
| 新建下载任务 | 四种模式切换、来源多选,以及最短时长、数量上限、并发数、请求间隔、目录组织、歌词等高级选项 |
| 预览清单 | 正式下载前先搜一遍,展示命中数量、去重结果和每首歌的可下载来源,已入库的会标记”已在库” |
| 任务面板 | 进度条 + 实时滚动日志,统计新下载、已在库、短音频、重复、失败的数量;刷新页面能自动接回正在跑的任务 |
| 音乐库 | 浏览全部已入库曲目,支持关键词过滤、内置播放器在线试听,也能连文件带索引一起删除 |
命令行
喜欢终端的话,四种模式对应四条命令:
1 | # 按歌手批量下载(多平台聚合,默认 wy/qq/kg) |
想先看看会下哪些、但不实际下载,可以加 --dry-run。其余像 --min-duration、--workers、--delay、--organize 这些选项,都是围绕”控制节奏、组织目录”来设计的。
接口是怎么来的
这部分其实是整个工具最关键、也最有意思的一环。
https://myhkw.cn/admin/#/ 后台需要注册登录,但官方还提供了一个免注册体验控制台 https://s.myhkw.cn/:只要访问首页,服务端就会自动下发一个匿名账号的 myhkid Cookie。我做的事情,就是用浏览器开发者工具把这个控制台实际调用的那组接口还原出来,然后复用它们。
还原出来之后,核心其实只有三个接口:
| 接口 | 作用 |
|---|---|
GET /action/search |
搜索,type 指定平台(wy/qq/kg/kw),加 gd 后缀就是按歌单 ID 取歌 |
GET /api/url |
取 / 下载 MP3,sign 来自搜索结果,服务器支持 Range,所以能断点续传 |
GET /api/lyrics |
取 LRC 歌词 |
如果你注册了正式账号,还可以用 --base https://myhkw.cn --cookie "..."(Cookie 从已登录的浏览器里复制)切到自己的账号上,拿到更高的配额。
也正因为接口来自一个公共服务,我在设计时刻意保持克制:并发默认建议 ≤3、相邻请求之间有最小间隔、同一时刻只允许跑一个下载任务,尽量别给对面添麻烦。
几个我觉得有意思的设计
三层去重
批量下载最怕的就是”同一首歌下三遍”。我用三层策略来兜底:
- 逻辑键:把曲名和歌手做规范化(全角转半角、去标点、剔除 Live / DJ / 伴奏 / 女声版这类版本标注)之后,只要相同就视为同一首歌,按来源优先级只保留第一个成功的。
- 内容哈希:下载完成后算一次 MD5,如果和库里任何文件内容相同,说明是换了个名字的同一首歌,直接删掉不保留。
- 持久索引:下载目录里维护一个
.myhkw_library.json,下次运行自动跳过已入库的曲目,Web 音乐库读的也是这份索引。
逻辑键负责”下载前就不重复挑”,内容哈希负责”换个马甲也认得出来”,持久索引负责”跨次运行不重做”,三层各司其职。
时长过滤
很多免费来源返回的其实是几十秒的高潮片段,甚至是纯静音占位。我的处理顺序是:
- 下载前直接用
ffprobe探测播放地址——因为服务器支持 Range,其实只要拉几 KB 就能拿到时长,不用把整首歌下完; - 短于阈值就跳过,并且自动换下一个来源再探测;
- 万一远程探测失败,就退回到”下载完成后本地复检”,不足阈值则删除。
这样既省流量,又尽量保证最后留下的都是完整版。
深色单文件前端
Web 那套界面是单个 index.html,深色主题、不依赖任何外部前端库。任务日志、进度、预览、曲库试听全部塞在一个页面里,配合 Flask 后端几个接口就够用了。对我来说,这种”够用就好、不引一堆构建工具”的方式反而最省心。
技术栈与项目结构
整个工具是纯 Python(3.9+),依赖很轻:requests 负责接口,flask 撑起 Web 界面,时长探测交给外部的 ffmpeg / ffprobe。结构大致是这样:
1 | ├── main.py # 入口 |
把”接口封装 / 下载编排 / 去重 / 探测”拆成独立模块,命令行和 Web 只是两套不同的入口,底层共用同一套逻辑,这样两种用法的行为能保持一致。
一点免责声明
这里要特别说明:明月浩空以及各音乐平台的资源版权,归属于各大音乐平台和相应权利人。这个项目仅用于个人学习与技术研究,请控制好请求频率,不要用于商业或传播用途,下载的内容建议在 24 小时内删除,或者去支持正版。
结语
回头看,这个工具真正花时间的地方,与其说是下载本身,不如说是”怎么把一堆不可靠的来源,整理成一个可信的曲库”——换源、去重、探测、节流,本质上都是在和”不确定的外部接口”打交道。
它现在还是主要服务于我自己的需求,一个”小而实用”的工具。代码开在 GitHub,如果哪天你又想把某个歌手的老歌一次性收进本地曲库,也许它能帮上忙。